全メンバーアカウントにおける全リージョンのAWS IAM Access Analyzerを一括削除してみた

全メンバーアカウントにおける全リージョンのAWS IAM Access Analyzerを一括削除してみた

Clock Icon2024.10.07

はじめに

クラスメソッドメンバーズのAWSアカウントをご利用の場合、セキュリティ強化とメンバーズサービス提供のため、複数のAWSサービスが自動的に有効化され、関連リソースが作成されます。

その一環として、cm-access-analyzerという名前のAWS IAM Access Analyzerが自動的に全リージョンで作成されます。このAnalyzerの信頼ゾーンは、アカウント単位で設定されています。

https://members.classmethod.net/members-docs/ja/resources/index.html?__hstc=53729015.c4a577029c49e44b73bd3bee6fa38565.1717027200314.1717027200315.1717027200316.1

以下の記事で解説されているように、マルチアカウント構成においては、通常、管理アカウントのみにAnalyzerを作成し、メンバーアカウントにはAnalyzerを作成しないケースが多いです。

https://dev.classmethod.jp/articles/multi-account-iam-access-analyzer/#toc-2

このため、各メンバーアカウントの全リージョンに存在するcm-access-analyzerを一括で削除する必要があります。本記事では、その効率的な方法をご紹介します。

実施手順の概要は以下の通りです。

  1. 管理アカウントにAssumeMemberRoleというIAMロールを作成し、メンバーアカウントのCustomOrganizationAccessRoleを引き受ける権限を付与します。
  2. メンバーアカウントにCustomOrganizationAccessRoleというIAMロールを作成し、IAM Access Analyzerの削除権限を付与します。
    この作業は、管理アカウントからCloudFormation StackSetsを使用して各メンバーアカウントに一括展開します。
  3. 管理アカウントから単一のコマンドを実行し、特定のOrganizational Unit (OU)配下の全アカウント内の全リージョンにあるcm-access-analyzerという名前のAnalyzerを削除します。

管理アカウントにIAMロール作成

まず、管理アカウントにIAMロールを作成し、メンバーアカウントのCustomOrganizationAccessRoleを引き受ける権限を付与します。

以下の操作は、AWS CloudShellを使用して実行します。

管理アカウントにAssumeMemberRoleというIAMロールを作成します。以下のAWS CLIコマンドを実行してください。

aws iam create-role --role-name AssumeMemberRole --assume-role-policy-document '{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "ec2.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}'

次に、作成したロールに、メンバーアカウントのCustomOrganizationAccessRoleを引き受ける権限を付与します。CustomOrganizationAccessRoleについては、後述します。
以下のコマンドを実行してください。

aws iam put-role-policy --role-name AssumeMemberRole --policy-name AssumeMemberRolePolicy --policy-document '{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": "arn:aws:iam::*:role/CustomOrganizationAccessRole"
        }
    ]
}'

メンバーアカウントにIAMロール作成

次に、メンバーアカウントにIAMロールを作成します。
この作業は、管理アカウントからCloudFormation StackSetsを使用して、OU配下の全アカウントに一括で適用します。

以下のCloudFormationテンプレートを使用してください。

CustomOrganizationAccessRole.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: Create IAM Role for Access Analyzer management

Parameters:
  ManagementAccountId:
    Type: String
    Description: "The AWS Account ID of the management account"

Resources:
  CustomOrganizationAccessRole:
    Type: "AWS::IAM::Role"
    Properties:
      RoleName: "CustomOrganizationAccessRole"
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              AWS: !Sub "arn:aws:iam::${ManagementAccountId}:root"
            Action: "sts:AssumeRole"
      Policies:
        - PolicyName: "AccessAnalyzerPolicy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action:
                  - "access-analyzer:CreateAnalyzer"
                  - "access-analyzer:DeleteAnalyzer"
                  - "access-analyzer:ListAnalyzers"
                  - "ec2:DescribeRegions"
                Resource: "*"
  1. 管理アカウントのAWS マネジメントコンソールからCloudFormation StackSetsにアクセスし、上記のテンプレートをアップロードします。
    cm-hirai-screenshot 2024-09-30 15.27.21
  2. テンプレートのパラメータ設定画面で、「ManagementAccountId」に管理アカウントのAWSアカウントIDを入力します。
    cm-hirai-screenshot 2024-09-30 15.27.41
  3. その他の設定はデフォルトのまま、「次へ」をクリックして進みます。
    cm-hirai-screenshot 2024-09-30 15.28.40
  4. デプロイオプションの設定画面で、「アカウントを選択」ではなく「組織単位を選択」を選び、全メンバーアカウントを含むOUのOU IDを指定します。cm-hirai-screenshot 2024-09-30 15.29.42
  5. デプロイするリージョンとして、東京リージョン(ap-northeast-1)のみを選択します。メンバーアカウント数は2つなので、同時アカウントの最大数は2、障害耐性は1にします。
    cm-hirai-screenshot 2024-10-03 8.00.15
    これで各メンバーアカウントにスタックが作成されます。

管理アカウントでアナライザー削除

AWS CloudShellでjqコマンドラインツールをインストールします。以下のコマンドを実行してください。

sudo yum install -y jq

OU配下の全アカウントの全リージョンにおけるIAM Access Analyzerの存在を確認します。以下のコマンドを実行してください。
コマンド内のou-xxxx-xxxxxxxxを実際のOU IDに置き換えてください。

ou_id="ou-xxxx-xxxxxxxx" && \
role_name="CustomOrganizationAccessRole" && \
regions=$(aws ec2 describe-regions --query Regions[*].RegionName --output text) && \

get_all_account_ids() {
    local ou_id=$1
    local accounts=$(aws organizations list-accounts-for-parent --parent-id $ou_id --query 'Accounts[*].Id' --output text)
    local child_ous=$(aws organizations list-children --parent-id $ou_id --child-type ORGANIZATIONAL_UNIT --query 'Children[*].Id' --output text)
    echo $accounts
    for child_ou in $child_ous; do get_all_account_ids $child_ou; done
} && \

assume_role() {
    local account_id=$1
    local creds=$(aws sts assume-role --role-arn arn:aws:iam::$account_id:role/$role_name --role-session-name check-analyzer-session)
    export AWS_ACCESS_KEY_ID=$(echo $creds | jq -r '.Credentials.AccessKeyId')
    export AWS_SECRET_ACCESS_KEY=$(echo $creds | jq -r '.Credentials.SecretAccessKey')
    export AWS_SESSION_TOKEN=$(echo $creds | jq -r '.Credentials.SessionToken')
} && \

check_analyzers() {
    local region=$1
    local analyzers=$(aws accessanalyzer list-analyzers --region $region --query 'analyzers[*].{name:name,type:type}' --output json)
    if [ "$(echo $analyzers | jq length)" -gt 0 ]; then
        echo "$analyzers" | jq -r '.[] | "IAM Access Analyzer \"\(.name)\" (Type: \(.type)) exists in '"$region"' for account '"$account_id"'"'
    else
        echo "No IAM Access Analyzers exist in $region for account $account_id"
    fi
} && \

member_accounts=$(get_all_account_ids $ou_id) && \

for account_id in $member_accounts; do
    echo "Processing account: $account_id" && \
    assume_role $account_id && \

    for region in $regions; do
        check_analyzers $region
    done && \

    unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
done

//出力結果
Processing account: 012345678901
IAM Access Analyzer "cm-access-analyzer" (Type: ACCOUNT) exists in ap-south-1 for account 012345678901
IAM Access Analyzer "cm-access-analyzer" (Type: ACCOUNT) exists in eu-north-1 for account 012345678901
IAM Access Analyzer "cm-access-analyzer" (Type: ACCOUNT) exists in eu-west-3 for account 012345678901
~中略~
Processing account: 123456789012
IAM Access Analyzer "cm-access-analyzer" (Type: ACCOUNT) exists in ap-south-1 for account 123456789012
IAM Access Analyzer "cm-access-analyzer" (Type: ACCOUNT) exists in eu-north-1 for account 123456789012
IAM Access Analyzer "cm-access-analyzer" (Type: ACCOUNT) exists in eu-west-3 for account 123456789012

確認が完了したら、以下のコマンドを使用して、OU配下の全アカウントの全リージョンにあるcm-access-analyzerという名前のIAM Access Analyzerを削除します。
コマンド内のou-xxxx-xxxxxxxxを実際のOU IDに置き換えてください。

ou_id="ou-xxxx-xxxxxxxx" && \
role_name="CustomOrganizationAccessRole" && \
regions=$(aws ec2 describe-regions --query Regions[*].RegionName --output text) && \
analyzer_name='cm-access-analyzer' && \

get_all_account_ids() {
    local ou_id=$1
    local accounts=$(aws organizations list-accounts-for-parent --parent-id $ou_id --query 'Accounts[*].Id' --output text)
    local child_ous=$(aws organizations list-children --parent-id $ou_id --child-type ORGANIZATIONAL_UNIT --query 'Children[*].Id' --output text)
    echo $accounts
    for child_ou in $child_ous; do get_all_account_ids $child_ou; done
} && \

assume_role() {
    local account_id=$1
    local creds=$(aws sts assume-role --role-arn arn:aws:iam::$account_id:role/$role_name --role-session-name delete-analyzer-session)
    export AWS_ACCESS_KEY_ID=$(echo $creds | jq -r '.Credentials.AccessKeyId')
    export AWS_SECRET_ACCESS_KEY=$(echo $creds | jq -r '.Credentials.SecretAccessKey')
    export AWS_SESSION_TOKEN=$(echo $creds | jq -r '.Credentials.SessionToken')
} && \

delete_analyzer() {
    local region=$1
    if aws accessanalyzer delete-analyzer --analyzer-name $analyzer_name --region $region >/dev/null 2>&1; then
        echo "IAM Access Analyzer '$analyzer_name' deleted in $region for account $account_id"
    else
        echo "IAM Access Analyzer '$analyzer_name' not found or failed to delete in $region for account $account_id"
    fi
} && \

member_accounts=$(get_all_account_ids $ou_id) && \

for account_id in $member_accounts; do
    echo "Processing account: $account_id" && \
    assume_role $account_id && \

    for region in $regions; do
        delete_analyzer $region
    done && \

    unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
done

//出力結果
Processing account: 012345678901
IAM Access Analyzer 'cm-access-analyzer' deleted in ap-south-1 for account 012345678901
IAM Access Analyzer 'cm-access-analyzer' deleted in eu-north-1 for account 012345678901
IAM Access Analyzer 'cm-access-analyzer' deleted in eu-west-3 for account 012345678901
~省略~

コマンドが正常に実行されると、各アカウントの各リージョンでのAnalyzer削除結果が表示されます。すべての削除操作が成功したことを確認してください。先程の確認コマンドを再度実行してもよいです。

参考として、削除したAnalyzerを再作成する必要がある場合に備え、OU配下の全アカウントの全リージョンにIAM Access Analyzer cm-access-analyzerを作成するコマンドを以下に示します。

ou_id="ou-xxxx-xxxxxxxx" && \
role_name="CustomOrganizationAccessRole" && \
regions=$(aws ec2 describe-regions --query Regions[*].RegionName --output text) && \
analyzer_name="cm-access-analyzer" && \

get_all_account_ids() {
    local ou_id=$1
    local accounts=$(aws organizations list-accounts-for-parent --parent-id $ou_id --query 'Accounts[*].Id' --output text)
    local child_ous=$(aws organizations list-children --parent-id $ou_id --child-type ORGANIZATIONAL_UNIT --query 'Children[*].Id' --output text)
    echo $accounts
    for child_ou in $child_ous; do get_all_account_ids $child_ou; done
} && \

assume_role() {
    local account_id=$1
    local creds=$(aws sts assume-role --role-arn arn:aws:iam::$account_id:role/$role_name --role-session-name create-analyzer-session)
    export AWS_ACCESS_KEY_ID=$(echo $creds | jq -r '.Credentials.AccessKeyId')
    export AWS_SECRET_ACCESS_KEY=$(echo $creds | jq -r '.Credentials.SecretAccessKey')
    export AWS_SESSION_TOKEN=$(echo $creds | jq -r '.Credentials.SessionToken')
} && \

create_analyzer() {
    local region=$1
    if aws accessanalyzer create-analyzer --analyzer-name $analyzer_name --type ACCOUNT --region $region >/dev/null 2>&1; then
        echo "IAM Access Analyzer '$analyzer_name' created in $region for account $account_id"
    else
        echo "IAM Access Analyzer '$analyzer_name' already exists or failed to create in $region for account $account_id"
    fi
} && \

member_accounts=$(get_all_account_ids $ou_id) && \

for account_id in $member_accounts; do
    echo "Processing account: $account_id" && \
    assume_role $account_id && \

    for region in $regions; do
        create_analyzer $region
    done && \

    unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
done

//出力結果
Processing account: 012345678901
IAM Access Analyzer 'cm-access-analyzer' created in ap-south-1 for account 012345678901
IAM Access Analyzer 'cm-access-analyzer' created in eu-north-1 for account 012345678901
IAM Access Analyzer 'cm-access-analyzer' created in eu-west-3 for account 012345678901
~省略~

アカウントIDを指定する

特定のアカウントのみでAnalyzerを削除または作成する必要がある場合、OU IDの代わりにアカウントIDを直接指定できます。
以下に、アカウントIDを使用してAnalyzerを確認と削除および作成するコマンドを示します。

以下のコマンドを使用して、指定したアカウントの全リージョンのAnalyzerを確認します。

確認
member_accounts=("000000000000" "111111111111") && \
role_name="CustomOrganizationAccessRole" && \
regions=$(aws ec2 describe-regions --query Regions[*].RegionName --output text) && \

assume_role() {
    local account_id=$1
    local creds=$(aws sts assume-role --role-arn arn:aws:iam::$account_id:role/$role_name --role-session-name check-analyzer-session)
    export AWS_ACCESS_KEY_ID=$(echo $creds | jq -r '.Credentials.AccessKeyId')
    export AWS_SECRET_ACCESS_KEY=$(echo $creds | jq -r '.Credentials.SecretAccessKey')
    export AWS_SESSION_TOKEN=$(echo $creds | jq -r '.Credentials.SessionToken')
} && \

check_analyzers() {
    local region=$1
    local analyzers=$(aws accessanalyzer list-analyzers --region $region --query 'analyzers[*].{name:name,type:type}' --output json)
    if [ "$(echo $analyzers | jq length)" -gt 0 ]; then
        echo "$analyzers" | jq -r '.[] | "IAM Access Analyzer \"\(.name)\" (Type: \(.type)) exists in '"$region"' for account '"$account_id"'"'
    else
        echo "No IAM Access Analyzers exist in $region for account $account_id"
    fi
} && \

for account_id in "${member_accounts[@]}"; do
    echo "Processing account: $account_id" && \
    assume_role $account_id && \

    for region in $regions; do
        check_analyzers $region
    done && \

    unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
done

以下のコマンドを使用して、指定したアカウントの全リージョンのAnalyzerを削除します。

削除
analyzer_name='cm-access-analyzer' && \
member_accounts=("000000000000" "111111111111") && \
role_name="CustomOrganizationAccessRole" && \
regions=$(aws ec2 describe-regions --query Regions[*].RegionName --output text) && \

assume_role() {
    local account_id=$1
    local creds=$(aws sts assume-role --role-arn arn:aws:iam::$account_id:role/$role_name --role-session-name delete-analyzer-session)
    export AWS_ACCESS_KEY_ID=$(echo $creds | jq -r '.Credentials.AccessKeyId')
    export AWS_SECRET_ACCESS_KEY=$(echo $creds | jq -r '.Credentials.SecretAccessKey')
    export AWS_SESSION_TOKEN=$(echo $creds | jq -r '.Credentials.SessionToken')
} && \

delete_analyzer() {
    local region=$1
    if aws accessanalyzer delete-analyzer --analyzer-name $analyzer_name --region $region >/dev/null 2>&1; then
        echo "IAM Access Analyzer '$analyzer_name' deleted in $region for account $account_id"
    else
        echo "IAM Access Analyzer '$analyzer_name' not found or failed to delete in $region for account $account_id"
    fi
} && \

for account_id in "${member_accounts[@]}"; do
    echo "Processing account: $account_id" && \
    assume_role $account_id && \

    for region in $regions; do
        delete_analyzer $region
    done && \

    unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
done

削除したAnalyzerを再作成する必要がある場合、以下のコマンドを使用します。

作成
analyzer_name='cm-access-analyzer' && \
member_accounts=("000000000000" "111111111111") && \
role_name="CustomOrganizationAccessRole" && \
regions=$(aws ec2 describe-regions --query Regions[*].RegionName --output text) && \

assume_role() {
    local account_id=$1
    local creds=$(aws sts assume-role --role-arn arn:aws:iam::$account_id:role/$role_name --role-session-name create-analyzer-session)
    export AWS_ACCESS_KEY_ID=$(echo $creds | jq -r '.Credentials.AccessKeyId')
    export AWS_SECRET_ACCESS_KEY=$(echo $creds | jq -r '.Credentials.SecretAccessKey')
    export AWS_SESSION_TOKEN=$(echo $creds | jq -r '.Credentials.SessionToken')
} && \

create_analyzer() {
    local region=$1
    if aws accessanalyzer create-analyzer --analyzer-name $analyzer_name --type ACCOUNT --region $region >/dev/null 2>&1; then
        echo "IAM Access Analyzer '$analyzer_name' created in $region for account $account_id"
    else
        echo "IAM Access Analyzer '$analyzer_name' already exists or failed to create in $region for account $account_id"
    fi
} && \

for account_id in "${member_accounts[@]}"; do
    echo "Processing account: $account_id" && \
    assume_role $account_id && \

    for region in $regions; do
        create_analyzer $region
    done && \

    unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
done

今後発行されるメンバーアカウント

今後発行されるメンバーアカウントもアナライザーは作成された状態でアカウントを引き渡されます。

今後発行されるメンバーアカウントについても、アナライザーが作成された状態でアカウントが引き渡されます。

メンバーアカウント内で実行するため、CustomOrganizationAccessRoleは利用しません。

analyzer_name='cm-access-analyzer'
for r in $(aws ec2 describe-regions --query Regions[*].RegionName --output text)
do
    aws accessanalyzer delete-analyzer --analyzer-name $analyzer_name --region $r
    echo "Succeeded in delete IAM Access Analyzer@ $r"
done

作成する場合は以下のコマンドです。

analyzer_name='cm-access-analyzer'
for r in $(aws ec2 describe-regions --query Regions[*].RegionName --output text)
do
    aws accessanalyzer delete-analyzer --analyzer-name $analyzer_name --region $r
    echo "Succeeded in delete IAM Access Analyzer@ $r"
done

最後に

本記事では、AWS Organizations内の全メンバーアカウントにおける全リージョンのIAM Access Analyzerを一括で削除する方法について解説しました。

具体的には、以下の流れです。

  1. 管理アカウントでのIAMロールの作成
  2. CloudFormation StackSetsを使用した各メンバーアカウントでIAMロールを作成
  3. 一括削除のためのスクリプト実行

参考になれば幸いです。

参考

https://dev.classmethod.jp/articles/script-to-create-delete-all-regions-of-iam-access-analyzer/

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.